package com.ruoyi.system.service;

import cn.hutool.core.util.ObjectUtil;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.constant.WeChatPayUrl;
import com.ruoyi.system.domain.WeChatPay;
import com.ruoyi.system.util.WxChatPayCommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信支付接口封装服务
 *
 * @author dida
 */
@Service
@Slf4j
public class WeChatPayService {
    private static final DecimalFormat DF = new DecimalFormat("#");

    /**
     * 统一下单接口
     *
     * @param weChatPay 微信支付对象
     * @return 包含订单结果的Map
     * @throws Exception 如果发生异常，则抛出异常
     */
    public Map<String, Object> unifiedorder(WeChatPay weChatPay) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();

        // 参数校验
        if (weChatPay == null || StringUtils.isEmpty(weChatPay.getAppid()) || StringUtils.isEmpty(weChatPay.getMch_id())) {
            throw new IllegalArgumentException("微信支付参数不完整");
        }
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        SortedMap<String, String> req = new TreeMap<>();
        req.put("appid", weChatPay.getAppid());
        req.put("mch_id", weChatPay.getMch_id());
        req.put("nonce_str", weChatPay.getNonce_str());
        req.put("body", weChatPay.getBody());
        req.put("out_trade_no", weChatPay.getOut_trade_no());
        req.put("total_fee", new BigDecimal(weChatPay.getTotal_fee()).multiply(new BigDecimal(100)).intValue() + "");
        req.put("spbill_create_ip", weChatPay.getSpbill_create_ip());
        req.put("notify_url", weChatPay.getNotify_url());
        req.put("trade_type", weChatPay.getTrade_type());
        req.put("attach", weChatPay.getAttach());


        if (StringUtils.isNotEmpty(weChatPay.getSub_mch_id())) {
            req.put("sub_mch_id", weChatPay.getSub_mch_id());
        }
        if (StringUtils.isNotEmpty(weChatPay.getTime_expire())) {
            req.put("time_expire", weChatPay.getTime_expire());
        }
        if (StringUtils.isNotEmpty(weChatPay.getOpenid())) {
            req.put("openid", weChatPay.getOpenid());
        }


        req.put("sign", WXPayUtil.generateSignature(req, weChatPay.getApi_key(), WXPayConstants.SignType.MD5));
        log.info("WeChat pay request params signed, req: {}", req);

        String xmlBody = WXPayUtil.generateSignedXml(req, weChatPay.getApi_key());
        log.info("WeChat pay resp params signed, xmlBody: {}", xmlBody);
        String result = WxChatPayCommonUtil.httpsRequest(WeChatPayUrl.Uifiedorder, "POST", xmlBody);

        log.info("WeChat pay result params signed, result: {}", result);

        if (StringUtils.isEmpty(result)) {
            log.error("WeChat pay request failed, outTradeNo: {}", weChatPay.getOut_trade_no());
            throw new RuntimeException("微信支付请求失败");
        }

        Map<String, String> wxResultMap = null;
        if (result != null) {
            wxResultMap = WXPayUtil.xmlToMap(result);
            log.info("wxResultMap: {}", wxResultMap);
        }

        if (wxResultMap != null && !"SUCCESS".equals(wxResultMap.get("return_code"))) {
            log.error("WeChat pay return failed, returnMsg: {}", wxResultMap.get("return_msg"));
            resultMap.put("code", 1);
            resultMap.put("msg", wxResultMap.get("return_msg"));
            return resultMap;
        }


        if (wxResultMap != null && "SUCCESS".equals(wxResultMap.get("result_code"))) {
            log.error("WeChat pay unifiedOrder success, outTradeNo: {}", weChatPay.getOut_trade_no());
            resultMap.put("code", 0);
            resultMap.put("msg", "预下单成功");
            resultMap.put("out_trade_no", weChatPay.getOut_trade_no());


            String tradeType = wxResultMap.get("trade_type");
            switch (tradeType) {
                case "NATIVE":
                    resultMap.put("qrCode", wxResultMap.get("code_url"));
                    break;
                case "MWEB":
                    resultMap.put("mwebUrl", wxResultMap.get("mweb_url"));
                    break;
                case "JSAPI":
                    resultMap.put("prepayId", wxResultMap.get("prepay_id"));
                    break;
            }
        }


        resultMap.put("appid", weChatPay.getAppid());
        resultMap.put("nonceStr", weChatPay.getNonce_str());
        resultMap.put("paySign", req.get("sign"));
        resultMap.put("timestamp", timestamp);
        resultMap.put("req", req);


        return resultMap;
    }


    /**
     * 微信支付异步回调通知接口
     *
     * @param request 请求对象，包含微信回调的信息
     * @param apiKey  api密钥，用于验证微信回调信息的签名
     * @return 包含验证结果和返回给微信的数据的Map
     * @throws IOException 读取请求数据失败时抛出
     * @throws Exception   解析xml数据或验证签名失败时抛出
     */
    public Map<String, Object> weChatPayCallback(HttpServletRequest request, String apiKey) throws Exception {
        if (request == null || apiKey == null || apiKey.isEmpty()) {
            throw new IllegalArgumentException("Request or apiKey cannot be null");
        }

        Map<String, Object> resultMap = new HashMap<>();
        String wxPayXmlData = WxChatPayCommonUtil.readXmlFromStream(request);

        if (wxPayXmlData.isEmpty()) {
            throw new RuntimeException("Empty wechat pay xml data");
        }

        Map<String, String> wxResultMap = WXPayUtil.xmlToMap(wxPayXmlData);
        if (wxResultMap.isEmpty()) {
            throw new RuntimeException("Invalid wechat pay xml data");
        }

        boolean signStatus = WXPayUtil.isSignatureValid(wxResultMap, apiKey);
        if (signStatus) {
            String returnWeChat = WxChatPayCommonUtil.setReturnXml("SUCCESS", "OK");
            resultMap.put("Verify", "YES");
            resultMap.put("returnWeChat", returnWeChat);
            resultMap.put("data", wxResultMap);
        } else {
            resultMap.put("Verify", "NO");
            resultMap.put("msg", "验签失败。");
        }
        return resultMap;
    }


    /**
     * 查询微信支付订单状态
     *
     * @param weChatPay 参数值appid 商户id等等
     * @return 支付状态
     */
    public static Map<String, Object> queryPayStatus(WeChatPay weChatPay) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        //todo 创建请求参数
        SortedMap<String, String> req = new TreeMap<String, String>();
        // 公众号ID
        req.put("appid", weChatPay.getAppid());
        // 商户号
        req.put("mch_id", weChatPay.getMch_id());
        //商户订单号
        req.put("out_trade_no", weChatPay.getOut_trade_no());
        // 随机字符串
        req.put("nonce_str", weChatPay.getNonce_str());
        if (ObjectUtil.isNotEmpty(weChatPay.getSub_mch_id())) {
            //子商户号 微信支付 分配的子商户号（服务商模式的时候填入）
            req.put("sub_mch_id", weChatPay.getSub_mch_id());
        }
        req.put("sign", WXPayUtil.generateSignature(req, weChatPay.getApi_key(), WXPayConstants.SignType.MD5));
        //todo 生成要发送的 xml
        String xmlBody = WXPayUtil.generateSignedXml(req, weChatPay.getApi_key());
        System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));
        //todo 调用查询订单支付状态 API
        String result = WxChatPayCommonUtil.httpsRequest(WeChatPayUrl.Orderquery, "POST", xmlBody);
        //todo 返回解析后的 map 数据
        Map<String, String> wxResultMap = null;
        if (result != null) {
            wxResultMap = WXPayUtil.xmlToMap(result);
        }
        //todo 判断通信状态 此字段是通信标识，非交易标识
        //当return_code为FAIL时返回信息为错误原因 ，例如 签名失败 参数格式校验错误
        if (wxResultMap != null && ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && "SUCCESS".equals(wxResultMap.get("return_code"))) {
            //todo 业务结果
            if ("SUCCESS".equals(wxResultMap.get("result_code"))) {
                //todo 状态查询成功
                resultMap.put("code", 0);
                resultMap.put("data", wxResultMap);
                switch (wxResultMap.get("trade_state")) {
                    case "SUCCESS":
                        //订单交易状态code
                        resultMap.put("OrderCode", wxResultMap.get("trade_state"));
                        resultMap.put("msg", "支付成功");
                        //商户订单号
                        resultMap.put("out_trade_no", wxResultMap.get("out_trade_no"));
                        //支付完成时间
                        resultMap.put("time_end", wxResultMap.get("time_end"));
                        //下单时候传过去的附加数据
                        resultMap.put("attach", wxResultMap.get("attach"));
                        break;
                    case "REFUND":
                        resultMap.put("msg", "转入退款");
                        break;
                    case "NOTPAY":
                        resultMap.put("msg", "未支付");
                        break;
                    case "CLOSED":
                        resultMap.put("msg", "已关闭");
                        break;
                    case "REVOKED":
                        resultMap.put("msg", "已撤销(刷卡支付)");
                        break;
                    case "USERPAYING":
                        resultMap.put("msg", "用户支付中");
                        break;
                    case "PAYERROR":
                        resultMap.put("msg", "支付失败(其他原因，如银行返回失败)");
                        break;
                    case "ACCEPT":
                        resultMap.put("msg", "已接收，等待扣款");
                        break;
                }
            } else {
                //todo 下单失败
                resultMap.put("code", 2);
                resultMap.put("msg", wxResultMap.get("err_code_des"));
            }
        }
        System.out.printf("%s%n", wxResultMap);
        return resultMap;
    }

}
